/**
 * Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.afterturn.easypoi.excel.export;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;

import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.base.BaseExportService;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum;
import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil;
import cn.afterturn.easypoi.util.PoiPublicUtil;

/**
 * Excel导出服务
 * 
 * @author JueYue 2014年6月17日 下午5:30:54
 */
public class ExcelExportService extends BaseExportService {

	// 最大行数,超过自动多Sheet
	private static int MAX_NUM = 60000;

	/**
	 * 创建表头和sheet名称
	 * @param entity Excel 导出参数
	 * @param sheet sheet
	 * @param workbook workbook对象(xls xlsx)
	 * @param excelParams cell的映射
	 * @return 建了多少行
	 */
	protected int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook, List<ExcelExportEntity> excelParams) {
		int rows = 0, fieldLength = getFieldLength(excelParams);
		if (entity.getTitle() != null) {
			rows += createTitle2Row(entity, sheet, workbook, fieldLength);
		}
		rows += createHeaderRow(entity, sheet, workbook, rows, excelParams);
		sheet.createFreezePane(0, rows, 0, rows);
		return rows;
	}

	/**
	 * 创建 表头改变
	 * 
	 * @param entity
	 *            Excel 导出参数
	 * @param sheet
	 *            sheet
	 * @param workbook
	 *            workbook对象(xls xlsx)
	 * @param fieldWidth
	 *            导出报表的字段总长度
	 * @return
	 */
	public int createTitle2Row(ExportParams entity, Sheet sheet, Workbook workbook, int fieldWidth) {

		Row row = sheet.createRow(0);
		row.setHeight(entity.getTitleHeight());
		createStringCell(row, 0, entity.getTitle(), getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
		for (int i = 1; i <= fieldWidth; i++) {
			createStringCell(row, i, "", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
		}
		sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, fieldWidth));
		if (entity.getSecondTitle() != null) {
			row = sheet.createRow(1);
			row.setHeight(entity.getSecondTitleHeight());
			CellStyle style = workbook.createCellStyle();
			style.setAlignment(CellStyle.ALIGN_RIGHT);
			createStringCell(row, 0, entity.getSecondTitle(), style, null);
			for (int i = 1; i <= fieldWidth; i++) {
				createStringCell(row, i, "", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
			}
			sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, fieldWidth));
			return 2;
		}
		return 1;
	}

	public void createSheet(Workbook workbook, ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Excel export start ,class is {}", pojoClass);
			LOGGER.debug("Excel version is {}", entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
		}
		if (workbook == null || entity == null || pojoClass == null || dataSet == null) {
			throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
		}
		try {
			List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
			// 得到所有字段
			Field[] fileds = PoiPublicUtil.getClassFields(pojoClass);
			ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
			String targetId = etarget == null ? null : etarget.value();
			getAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, null, null);
			// 获取所有参数后,后面的逻辑判断就一致了
			createSheetForMap(workbook, entity, excelParams, dataSet);
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());
		}
	}

	public void createSheetForMap(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList, Collection<?> dataSet) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Excel version is {}", entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
		}
		if (workbook == null || entity == null || entityList == null || dataSet == null) {
			throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
		}
		super.type = entity.getType();
		if (type.equals(ExcelType.XSSF)) {
			MAX_NUM = 1000000;
		}
		if (entity.getMaxNum() > 0) {
			MAX_NUM = entity.getMaxNum();
		}
		Sheet sheet = null;
		try {
			sheet = workbook.createSheet(entity.getSheetName());
		} catch (Exception e) {
			// 重复遍历,出现了重名现象,创建非指定的名称Sheet
			sheet = workbook.createSheet();
		}
		insertDataToSheet(workbook, entity, entityList, dataSet, sheet);
	}

	/**
	 * 表格写入数据
	 * @param workbook workbokk对象(xls xlsx)
	 * @param entity 定义导出的参数
	 * @param entityList cell映射
	 * @param dataSet 数据list
	 * @param sheet sheet
	 */
	protected void insertDataToSheet(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList, Collection<?> dataSet, Sheet sheet) {
		try {
			dataHanlder = entity.getDataHanlder();
			if (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) {
				needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());
			}
			// 创建表格样式
			setExcelExportStyler((IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook));
			Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
			List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
			if (entity.isAddIndex()) {
				excelParams.add(indexExcelEntity(entity));
			}
			excelParams.addAll(entityList);
			sortAllParams(excelParams);
			int index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;
			int titleHeight = index;
			setCellWith(excelParams, sheet);
			setColumnHidden(excelParams, sheet);
			short rowHeight = entity.getHeight() > 0 ? entity.getHeight() : getRowHeight(excelParams);
			setCurrentIndex(1);
			Iterator<?> its = dataSet.iterator();
			List<Object> tempList = new ArrayList<Object>();
			while (its.hasNext()) {
				Object t = its.next();
				index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);
				tempList.add(t);
				if (index >= MAX_NUM) {
					break;
				}
			}
			if (entity.getFreezeCol() != 0) {
				sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
			}

			mergeCells(sheet, excelParams, titleHeight);

			its = dataSet.iterator();
			for (int i = 0, le = tempList.size(); i < le; i++) {
				its.next();
				its.remove();
			}
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("List data more than max ,data size is {}", dataSet.size());
			}
			// 发现还有剩余list 继续循环创建Sheet
			if (dataSet.size() > 0) {
				createSheetForMap(workbook, entity, entityList, dataSet);
			} else {
				// 创建合计信息
				addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
			}

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());
		}
	}

/**
 * 创建表头
 * @param title 表头
 * @param sheet sheet对象
 * @param workbook workbook对象
 * @param index 行数的索引(0开始)
 * @param excelParams cell的映射
 * @return
 */
	private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index, List<ExcelExportEntity> excelParams) {
		Row row = sheet.createRow(index);
		int rows = getRowNums(excelParams);
		row.setHeight(title.getHeaderHeight());
		Row listRow = null;
		if (rows == 2) {
			listRow = sheet.createRow(index + 1);
			listRow.setHeight(title.getHeaderHeight());
		}
		int cellIndex = 0;
		int groupCellLength = 0;
		CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor());
		for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
			ExcelExportEntity entity = excelParams.get(i);
			// 加入换了groupName或者结束就，就把之前的那个换行
			if (StringUtils.isBlank(entity.getGroupName()) || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) {
				if (groupCellLength > 1) {
					sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
				}
				groupCellLength = 0;
			}
			if (StringUtils.isNotBlank(entity.getGroupName())) {
				createStringCell(row, cellIndex, entity.getGroupName(), titleStyle, entity);
				createStringCell(listRow, cellIndex, entity.getName(), titleStyle, entity);
				groupCellLength++;
			} else if (StringUtils.isNotBlank(entity.getName())) {
				createStringCell(row, cellIndex, entity.getName(), titleStyle, entity);
			}
			if (entity.getList() != null) {
				List<ExcelExportEntity> sTitel = entity.getList();
				if (StringUtils.isNotBlank(entity.getName())) {
					sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex, cellIndex + sTitel.size() - 1));
				}
				for (int j = 0, size = sTitel.size(); j < size; j++) {
					createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(), titleStyle, entity);
					cellIndex++;
				}
				cellIndex--;
			} else if (rows == 2 && StringUtils.isBlank(entity.getGroupName())) {
				createStringCell(listRow, cellIndex, "", titleStyle, entity);
				sheet.addMergedRegion(new CellRangeAddress(index, index + 1, cellIndex, cellIndex));
			}
			cellIndex++;
		}
		if (groupCellLength > 1) {
			sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
		}
		return rows;

	}

}
